#include "imageinstancedata.h"

#include "../mainframe/mainshare.h"
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmimgle/dcmimage.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcuid.h"
#include "dcmtk/dcmdata/dcdict.h"
#include "dcmtk/ofstd/offname.h"
#include "Windows.h"

#include <QFile>

ImageInstanceData::ImageInstanceData(const QString &file):
    dcmff(0),
    dcmImage(0),
    filePath(file)
{
    DcmDataDictionary &dict = dcmDataDict.wrlock();
    if (!dict.isDictionaryLoaded()) {
        dict.reloadDictionaries(true, false);
    }
    dcmDataDict.unlock();

    OFFilename ofFile(file.toUtf8().data(), true);
    dcmff = new DcmFileFormat;
    dcmff->loadFile(ofFile);
    initImage();
}

ImageInstanceData::ImageInstanceData(DcmFileFormat *dff):
    dcmff(dff),
    dcmImage(0)
{
    initImage();
}

ImageInstanceData::~ImageInstanceData()
{
    delete dcmImage;
    delete dcmff;
}

bool ImageInstanceData::isNormal() const
{
    return dcmImage && (dcmImage->getStatus() == EIS_Normal);
}

void ImageInstanceData::initImage()
{
    DcmDataset *dset;
    OFCondition result;
    if (dcmff && (dset = dcmff->getDataset())) {
        //patient information
        const char *value = NULL;
        result = dset->findAndGetString(DCM_PatientID, value);
        patientID = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_PatientName, value);
        patientName = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_PatientAge, value);
        patientAge = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_PatientBirthDate, value);
        patientBirth = QDate::fromString(QString::fromLocal8Bit(value),
                                         DATE_DICOM_FORMAT);

        result = dset->findAndGetString(DCM_PatientSex, value);
        patientSex = QString::fromLocal8Bit(value);

        //study information
        result = dset->findAndGetString(DCM_StudyInstanceUID, value);
        studyUid = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_StudyDate, value);
        studyTime.setDate(QDate::fromString(QString::fromLocal8Bit(value),
                                            DATE_DICOM_FORMAT));

        result = dset->findAndGetString(DCM_StudyTime, value);
        studyTime.setTime(formatDicomTime(QString::fromLatin1(value)));

        result = dset->findAndGetString(DCM_StudyDescription, value);
        studyDes = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_ProtocolName, value);
        procId = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_BodyPartExamined, value);
        bodyPart = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_PatientPosition, value);
        bodyPos = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_Manufacturer, value);
        manufacturer = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_Modality, value);
        modality = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_ManufacturerModelName, value);
        modelName = QString::fromLocal8Bit(value);

        //series information
        result = dset->findAndGetString(DCM_SeriesInstanceUID, value);
        seriesUid = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_SeriesNumber, value);
        seriesNumber = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_SeriesDescription, value);
        seriesDes = QString::fromLocal8Bit(value);

        //instance information
        result = dset->findAndGetString(DCM_SOPClassUID, value);
        sopClassUid = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_SOPInstanceUID, value);
        instanceUid = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_InstanceNumber, value);
        instanceNumber = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_InstitutionName, value);
        institution = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_AcquisitionDate, value);
        if (value==NULL) result = dset->findAndGetString(DCM_ContentDate, value);
        acquisitionTime.setDate(QDate::fromString(QString::fromLatin1(value),
                                                  DATE_DICOM_FORMAT));

        result = dset->findAndGetString(DCM_AcquisitionTime, value);
        if (value==NULL) result = dset->findAndGetString(DCM_ContentTime, value);
        acquisitionTime.setTime(formatDicomTime(QString::fromLatin1(value)));

        result = dset->findAndGetString(DCM_PatientPosition, value);
        patientPostion = QString::fromLatin1(value);

        result = dset->findAndGetString(DCM_RequestingPhysician, value);
        reqPhysician = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_PerformingPhysicianName, value);
        perPhysician = QString::fromLocal8Bit(value);

        result = dset->findAndGetString(DCM_AccessionNumber, value);
        accessionNumber = QString::fromLatin1(value);

        result = dset->findAndGetFloat64(DCM_PixelSpacing, pixelSpacingY, 0);
        result = dset->findAndGetFloat64(DCM_PixelSpacing, pixelSpacingX, 1);
        result = dset->findAndGetFloat64(DCM_SliceThickness, sliceThickness);
        result = dset->findAndGetFloat64(DCM_ImagePositionPatient, startX, 0);
        result = dset->findAndGetFloat64(DCM_ImagePositionPatient, startY, 1);
        result = dset->findAndGetFloat64(DCM_SliceLocation, sliceLocation);
        result = dset->findAndGetFloat64(DCM_RescaleIntercept, intercept);

        result = dset->findAndGetString(DCM_KVP, value);
        kvp = QString::fromLatin1(value).toDouble();
        result = dset->findAndGetString(DCM_Exposure, value);
        mAs = QString::fromLatin1(value).toDouble();
        result = dset->findAndGetString(DCM_ExposureTime, value);
        mS = QString::fromLatin1(value).toDouble();
        result = dset->findAndGetString(DCM_XRayTubeCurrent, value);
        mA = QString::fromLatin1(value).toDouble();

        result = dset->findAndGetFloat64(DCM_WindowWidth, winWidth);
        result = dset->findAndGetFloat64(DCM_WindowCenter, winCenter);

        dcmImage = new DicomImage(dcmff, dset->getOriginalXfer());
        if (dcmImage->getStatus() == EIS_Normal) {
            imgWidth = dcmImage->getWidth();
            imgHeight = dcmImage->getHeight();
            if (winWidth < 1) {
                dcmImage->setRoiWindow(0, 0, imgWidth, imgHeight);
                dcmImage->getWindow(winCenter, winWidth);
            }
        }
    }
}

const short *ImageInstanceData::getInternalPtr() const
{
    return dcmImage?(const short*)dcmImage->getInterData()->getData():0;
}

const ushort *ImageInstanceData::getRawData() const
{
    if (isNormal()) {
        const ushort *data = 0;
        ulong count;
        dcmff->getDataset()->findAndGetUint16Array(DCM_PixelData, data, &count);
        return data;
    }
    return 0;
}

void ImageInstanceData::setRoiWindow(const QRectF &rect)
{
        dcmImage->setRoiWindow(rect.left(), rect.top(), rect.width(), rect.height());
        dcmImage->getWindow(winCenter, winWidth);
}

void ImageInstanceData::setPolarity(EP_Polarity polarity)
{
    dcmImage->setPolarity(polarity);
}

EP_Polarity ImageInstanceData::getPolarity() const
{
    if (dcmImage) return dcmImage->getPolarity();
    return EPP_Normal;
}

const DicomImage *ImageInstanceData::getDcmImage() const
{
    return dcmImage;
}

DcmFileFormat *ImageInstanceData::getFileFormat() const
{
    return dcmff;
}

bool ImageInstanceData::saveFileFormat()
{
    DcmDataset *dset;
    DicomImage *image, *tmp;
    if (dcmff) {
        dset = dcmff->getDataset();
        image = new DicomImage(dcmff, dset->getOriginalXfer());
        tmp = dcmImage;
        dcmImage = image;
        delete tmp;
        dset->findAndGetFloat64(DCM_WindowWidth, winWidth);
        dset->findAndGetFloat64(DCM_WindowCenter, winCenter);
        dcmImage->setWindow(winCenter, winWidth);
        if (!filePath.isEmpty())
            return EC_Normal == dcmff->saveFile(filePath.toLocal8Bit().data());
    }
    return false;
}

bool ImageInstanceData::dcm2bmpHelper(DicomImage &dcmImage, QPixmap &pixmap)
{
    BITMAPFILEHEADER lpfh;
    BITMAPINFOHEADER lpih;
    RGBQUAD palette[256];

    memset(&lpfh, 0, sizeof(BITMAPFILEHEADER));
    lpfh.bfType = 0x4d42;  //'B''M'
    lpfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(palette);


    memset(&lpih, 0, sizeof(BITMAPINFOHEADER));
    lpih.biSize = sizeof(BITMAPINFOHEADER);
    lpih.biWidth = dcmImage.getWidth();
    lpih.biHeight = dcmImage.getHeight();
    lpih.biCompression = BI_RGB;
    lpih.biBitCount = 8;
    lpih.biPlanes = 1;

    memset(palette, 0, sizeof(palette));
    for (int i = 0; i < 256; ++i) {
        palette[i].rgbBlue = i;
        palette[i].rgbGreen = i;
        palette[i].rgbRed = i;
    }

    void *pDIB = NULL;

    int size = dcmImage.createWindowsDIB(pDIB, 0, 0, 8, 1, 1);

    //lpih.biSizeImage = size;
    lpfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(palette) + size;

    QByteArray bmp;
    bmp.append((char*)&lpfh, sizeof(BITMAPFILEHEADER));
    bmp.append((char*)&lpih, sizeof(BITMAPINFOHEADER));
    bmp.append((char*)palette, sizeof(palette));
    bmp.append((char*)pDIB, size);

    delete pDIB;
    return pixmap.loadFromData(bmp);
}

bool ImageInstanceData::getPixmap(QPixmap &pixmap)
{
    if (isNormal()) {
        dcmImage->setWindow(winCenter, winWidth);
        return dcm2bmpHelper(*dcmImage, pixmap);
    }
    return false;
}

bool ImageInstanceData::getPixmap(const QString &dicomFile, QPixmap &pixmap)
{
    ImageInstanceData image(dicomFile);
    return image.getPixmap(pixmap);
}

DicomImage* ImageInstanceData::createClippedImage(const QRect &rect, int angle, bool hflip, bool vflip, bool inverted)
{
    DicomImage *image = dcmImage;;
    if (!image) return image;

    int ret = 1;
    Uint16 pvalue = image->getPhotometricInterpretation()==EPI_Monochrome1?65535:0;
    DicomImage *newImage = image->createClippedImage(rect.left(), rect.top(), rect.width(), rect.height(), pvalue);
    if (newImage) {
        if (ret && angle) ret = newImage->rotateImage(angle%360);
        if (ret && hflip) ret = newImage->flipImage(1, 0);
        if (ret && vflip) ret = newImage->flipImage(0, 1);
        if (ret && inverted) ret = newImage->setPolarity(EPP_Reverse);
        if (!ret) {
            delete newImage;
            newImage = 0;
        }
    }
    return newImage;
}

QStringList ImageInstanceData::getAnnotations() const
{
    QString topleft;
    QString topright;
    QString bottomleft;
    QString bottomright;

    topleft = QString("%1%2Scalor: 100mm").arg(studyDes.isEmpty()?studyDes:(studyDes+"\n"),
                                               seriesDes.isEmpty()?seriesDes:(seriesDes+"\n"));
    topright = QString("%1%2%3%4").arg(patientName.isEmpty()?"":(patientName+"\n"),
                                       patientID.isEmpty()?"":(patientID+"\n"),
                                       patientSex.isEmpty()?"":(patientSex+"/"), patientAge);

    QString bodyInfo = QString("%1-%2").arg(bodyPart, bodyPos);
    if (bodyInfo.startsWith("-")) bodyInfo.remove(0, 1);
    if (bodyInfo.endsWith("-")) bodyInfo.remove(bodyInfo.size()-1, 1);
    bottomleft = QString("%1%2").arg(bodyInfo.isEmpty()?"":(bodyInfo+"\n"), manufacturer);
    bottomright = QString("%3%4").arg(acquisitionTime.isValid()?acquisitionTime.toString(DATETIME_SQLITE_FORMAT):"",
                                  (acquisitionTime.isValid()&&(!institution.isEmpty()))?("\n"+institution):"");
    return QStringList() << topleft << topright << bottomleft << bottomright;
}

uint ImageInstanceData::getPixelValue(long x, long y, EP_Representation &r) const
{
    DicomImage *image = dcmImage;

    if (image) {
        const DiPixel* pixel = image->getInterData();
        if (pixel && (x < (long)image->getWidth()) && (x >= 0)
                && (y < (long)image->getHeight()) && (y >= 0)) {
            r = pixel->getRepresentation();
            switch (r) {
            case EPR_Sint8:
            case EPR_Uint8:
                return *((char*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Sint16:
            case EPR_Uint16:
                return *((short*)(pixel->getData()) + (y * image->getWidth() + x));
            case EPR_Sint32:
            case EPR_Uint32:
                return *((int*)(pixel->getData()) + (y * image->getWidth() + x));
            }
        }
    }

    r = (EP_Representation)-1;
    return 0;
}

bool ImageInstanceData::getPixSpacing(double &spacingX, double &spacingY, double &spacingZ) const
{
    if (isNormal()) {
        spacingX = pixelSpacingX;
        spacingY = pixelSpacingY;
        spacingZ = sliceThickness;
        return true;
    }
    return false;
}

bool ImageInstanceData::getImagePosition(double &x, double &y, double &z) const
{
    if (isNormal()) {
        x = startX;
        y = startY;
        z = sliceLocation;
        return true;
    }
    return false;
}

bool ImageInstanceData::getExposureParam(double &kv, double &ma, double &ms) const
{
    if (isNormal()) {
        kv = kvp;
        ma = mA;
        ms = mS;
        return true;
    }
    return false;
}

bool ImageInstanceData::getImageSize(ulong &width, ulong &height) const
{
    if (isNormal()) {
        width = imgWidth;
        height = imgHeight;
        return true;
    }
    return false;
}
